﻿using System;
using System.ComponentModel;
using System.Web.Mvc;
using System.Web.Routing;
using AutoMapper;
using ExampleWebApplication.Entities;
using ExampleWebApplication.Infraestruture;
using ExampleWebApplication.Infraestruture.NHibernate;
using ExampleWebApplication.Models;
using TelerikMvcGridCustomBindingHelper.Mapper;
using TelerikMvcGridCustomBindingHelper.NHibernate;

namespace ExampleWebApplication
{
    public class MvcApplication : System.Web.HttpApplication
    {
        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();

            RegisterGlobalFilters(GlobalFilters.Filters);
            RegisterRoutes(RouteTable.Routes);

            CreateMaps(true);
        }

        //private static string GetDefaultSortMemberName(Type type)
        //{
        //    return type.Name + "ID";
        //}

        public static void CreateMaps(bool nhibernateIntegration)
        {
            GridModelMapper.Configuration.DefaultSortMemberName(IDMemberName.EntityNamePlusId, ListSortDirection.Descending);
            //GridModelMapper.Configuration.DefaultSortMemberName(MemberName.EntityNamePlusSuffix, "id", ListSortDirection.Descending);
            //GridModelMapper.Configuration.DefaultSortMemberName(MemberName.Guess, ListSortDirection.Descending);
            //GridModelMapper.Configuration.DefaultSortMemberName(GetDefaultSortMemberName, ListSortDirection.Descending);

            GridModelMapper.Configuration.CreateSimpleMappingsAsNeeded();
            // If you call CreateSimpleMappingsAsNeeded() method
            // you dont need to specify the following mapping, but
            // pay attention, *Product must not have nested unmapped members*
            // the mappings will be created dymicaly
            // Mapper.CreateMap<Product, ProductModel>();

            // for simple maps like this, the GMM should be enought
            GridModelMapper.CreateMap<Category, CategoryModel>();
            GridModelMapper.CreateMap<CategoryModel, Category>();

            // GMM and AM can work together, the mappings are not overriden by each other
            Mapper.CreateMap<Order, OrderModel>()
                .ForMember(trg => trg.Id, opt => opt.MapFrom(src => src.OrderID))
                .ForMember(trg => trg.RecipientName, opt => opt.MapFrom(src => src.ShipName))
                .ForMember(trg => trg.RecipientAddress, opt => opt.MapFrom(src => src.ShipAddress))
                .ForMember(trg => trg.RecipientCity, opt => opt.MapFrom(src => src.ShipCity));
            GridModelMapper.CreateMap<Order, OrderModel>()
                .MapProperty(entity => entity.Customer.ContactName, viewModel => viewModel.Customer.CustomerName)
                //.MapProperty(entity => entity.Employee.LastName, viewModel => viewModel.Employee.EmployeeName)
                .MapProperty(entity => entity.Employee, viewModel => viewModel.Employee)
                .MapProperty(entity => entity.Shipper.CompanyName, viewModel => viewModel.Shipper.CompanyName);

            Mapper.CreateMap<Customer, CustomerModel>()
                .ForMember(trg => trg.CustomerName, opt => opt.MapFrom(src => src.ContactName));

            Mapper.CreateMap<Employee, EmployeeModel>()
                .ForMember(trg => trg.EmployeeName, opt => opt.MapFrom(src => src.FirstName + " " + src.LastName));

            Mapper.CreateMap<Shipper, ShipperModel>()
                .ForMember(trg => trg.CompanyName, opt => opt.MapFrom(src => src.CompanyName));

            // Add a BooleanSubstitute for a specifique property
            GridModelMapper.CreateMap<Customer, CustomerModel>()
                // For EntityFramework
                //.MapProperty(customer => customer.EfBool, model => model.EfBool).WithOptions(options => options.WithBooleanSubstitute<EfBooleanSubstitute>())
                .MapProperty(customer => customer.EfBool, model => model.EfBool, options => options.SetBooleanSubstitute<EfBooleanSubstitute>())
                // For NHibernate
                //.MapProperty(customer => customer.Bool, model => model.Bool).WithOptions(options => options.WithBooleanSubstitute<BooleanSubstitute>())
                .MapProperty(customer => customer.Bool, model => model.Bool, options => options.SetBooleanSubstitute<BooleanSubstitute>());

            // Set a GlobalBooleanSubstitute for all boolean properties
            //GridModelMapper.Configuration.SetGlobalBooleanSubstitute<GlobalBooleanSubstitute>();

            // Add a DecimalValueResolver for a specifique property
            GridModelMapper.CreateMap<Order, OrderModel>()
                //.MapProperty(order => order.Freight, model => model.Freight).WithOptions(options => options.ResolveUsing<DecimalValueResolver>());
                .MapProperty(order => order.Freight, model => model.Freight, options => options.SetValueResolver<DecimalValueResolver>());

            // Globally set AcceptNullValuesWhenFiltering for all entities
            //GridModelMapper.Configuration.AcceptNullValuesWhenFiltering();

            // Globally set AcceptNullValuesWhenFiltering for a specifique entity
            //GridModelMapper.Configuration.AcceptNullValuesWhenFiltering<Product>();

            // AcceptNullValuesWhenFiltering for a specifique property
            GridModelMapper.CreateMap<Order, OrderModel>()
                //.MapProperty(order => order.ShipRegion, model => model.ShipRegion).WithOptions(options => options.AcceptNullValuesWhenFiltering())
                //.MapProperty(order => order.ShipRegion, model => model.ShipRegion, options => options.AcceptNullValuesWhenFiltering());
                .MapProperty(order => order.ShipRegion, model => model.ShipRegion, options => options.SetNullAlias<ShipRegionNullAlias>());

            //GridModelMapper.Configuration.SetGlobalNullSubstitute<EmployeeRegionNullSubstitute>();

            GridModelMapper.CreateMap<Employee, EmployeeModel>()
                .MapProperty(employee => employee.FirstName + " " + employee.LastName, model => model.EmployeeName)
                .MapProperty(employee => employee.Region, model => model.Region, options => options.SetNullSubstitute<EmployeeRegionNullSubstitute>());

            // Add a GlobalDecimalValueResolver for any property
            //GridModelMapper.Configuration.AddGlobalValueResolver<GlobalDecimalValueResolver>();

            GridModelMapper.CreateMap<Product, ProductModel>()
                .MapProperty(product => product.Units_On_Order, productModel => productModel.Units_OnOrder)
                .MapProperty(product => product.ProductName, productModel => productModel.ViewModelProp);
            Mapper.CreateMap<Product, ProductModel>().ForMember(trg => trg.ViewModelProp, opt => opt.MapFrom(src => "[Agreggates] " + src.ProductName));

            //GridModelMapper.Configuration.UseCaseInsensitiveSearch();

            if (nhibernateIntegration)
            {
                try
                {
                    // Generic ORM integration: you have the control (did not work with TelerikMvcGridCustomBindingHelper.NHibernate!)
                    // Use projections to avoid "SelectNPlusOne"
                    //GridModelMapper.Configuration.StartORMIntegration<ORMIntegration>().UseTransactons();
                    //GridModelMapper.Configuration.StartORMIntegration(() => new ORMIntegration()).UseTransactons();
                    //GridModelMapper.Configuration.StartORMIntegration(new ORMIntegration()).UseTransactons();

                    // Custom NHibernate integration (works with TelerikMvcGridCustomBindingHelper.NHibernate!)
                    //GridModelMapper.Configuration.StartNHibernateIntegration<NHibernateIntegration>().UseTransactons();

                    // Native NHibernate integration (in this case the helper will need a working SessionFactory!)
                    GridModelMapper.Configuration.StartNHibernateIntegration(NHibernateHelper.GetSessionFactory()).UseTransactions();
                }
                catch
                {
                    Console.WriteLine("NHibernate failed to start!");
                }
            }
        }

        private static void RegisterGlobalFilters(GlobalFilterCollection filters)
        {
            filters.Add(new HandleErrorAttribute());
        }

        private static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

            routes.MapRoute(
                "Default", // Route name
                "{controller}/{action}/{id}", // URL with parameters
                new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
            );
        }
    }
}